/**
 * @author:cavion(曹仁道)
 * @email:caorendao187@163.com
 * 2018年6月9日
 */
package org.rk.core.auth.config;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.rk.core.auth.filter.RKFormAuthenticationFilter;
import org.rk.core.auth.realm.RKAuthRealm;
import org.rk.core.common.constant.RkConst;
import org.rk.core.common.util.RkObjectUtil;
import org.rk.core.common.util.RkStrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.Environment;

/**
 * @author cavion
 * @描述:shiro拦截
 * 2018年6月9日 下午7:51:22
 */
@Configuration
public class ShiroConfiguration {
	private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);
	private static Map<String, String> filterChainDefinitionMap = null;
	@Autowired
	private Environment env;

	public Environment getEnv() {
		return env;
	}

	public void setEnv(Environment env) {
		this.env = env;
	}
	 /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意：单独一个ShiroFilterFactoryBean配置是或报错的，以为在
     * 初始化ShiroFilterFactoryBean的时候需要注入：SecurityManager
     *
     * Filter Chain定义说明 1、一个URL可以配置多个Filter，使用逗号分隔 2、当设置多个过滤器时，全部验证通过，才视为通过
     * 3、部分过滤器可指定参数，如perms，roles
     *
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/views/login/login_signin.html");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        //自定义拦截器
        Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
        //重写FormAuthenticationFilter拦截器,对ajax请求处理以及验证码处理
        filtersMap.put(RkConst.auth_key.filterName_auth, authcSessionFilter());
        //计划重写登出拦截器的
        filtersMap.put(RkConst.auth_key.filterName_logout, logoutSessionFilter());
        shiroFilterFactoryBean.setFilters(filtersMap);

        // 权限控制map
        //过滤链定义，从上向下顺序执行，一般将 /**放在最为下边
        //authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
        if(RkObjectUtil.isEmpty(filterChainDefinitionMap)) {
        	filterChainDefinitionMap = new LinkedHashMap<String, String>();
        	addFilterChain(filterChainDefinitionMap, RkConst.auth_key.filterName_logout);
            addFilterChain(filterChainDefinitionMap, RkConst.auth_key.filterName_auth);
            addFilterChain(filterChainDefinitionMap, RkConst.auth_key.filterName_anon);
        }
        
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 
     * @author：cavion
     * @描述：设置调用链
     * 2018年6月9日 下午9:29:03
     * @param filterChainDefinitionMap
     */
    private void addFilterChain(Map<String, String> filterChainDefinitionMap, String filterName){
    	//从配置properties中提取
        String authFilters = env.getProperty(filterName);
        String[] authFilterArr = authFilters.split(",");
        for (String authFilterUrl : authFilterArr) {
        	filterChainDefinitionMap.put(authFilterUrl, filterName);
		}
    }
    
    @Bean
    public RKFormAuthenticationFilter authcSessionFilter() {
    	return new RKFormAuthenticationFilter();
    }
    @Bean
    public LogoutFilter logoutSessionFilter() {
    	LogoutFilter logoutFilter = new LogoutFilter();
    	logoutFilter.setRedirectUrl("/views/login/login_signin.html");
    	return logoutFilter;
    }
    
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(myShiroRealm());
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        // 自定义session管理 使用redis
        //securityManager.setSessionManager(sessionManager());
        //注入记住我管理器;
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    public RKAuthRealm myShiroRealm(){
        return new RKAuthRealm();
    }

    /**
     * cookie对象;
     * @return
     */
    public SimpleCookie rememberMeCookie(){
       //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
       SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
       //<!-- 记住我cookie生效时间30天 ,单位秒;-->
       simpleCookie.setMaxAge(2592000);
       return simpleCookie;
    }

    /**
     * cookie管理对象;记住我功能
     * @return
     */
    public CookieRememberMeManager rememberMeManager(){
       CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
       cookieRememberMeManager.setCookie(rememberMeCookie());
       //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
       cookieRememberMeManager.setCipherKey(Base64.decode("cavioncaorendao187@163.com=="));
       return cookieRememberMeManager;
    }
    
    public CacheManager cacheManager() {
    	logger.debug("#########################初始化session共享缓存redis######################");
    	RedisCacheManager cacheManager = new RedisCacheManager();
    	RedisManager redisManager = new RedisManager();
    	String host = env.getProperty(RkConst.session_key.sessionCacheRedisHost);
    	if(RkStrUtil.isNotEmpty(host)) {
    		redisManager.setHost(host);
    	}
    	redisManager.setHost(host);
    	String port = env.getProperty(RkConst.session_key.sessionCacheRedisPort);
    	if(RkStrUtil.isNotEmpty(port)) {
    		redisManager.setPort(Integer.parseInt(port));
    	}
    	String expire = env.getProperty(RkConst.session_key.sessionCacheRedisExpire);
    	if(RkStrUtil.isNotEmpty(expire)) {
    		redisManager.setExpire(Integer.parseInt(expire));
    	}
    	String database = env.getProperty(RkConst.session_key.sessionCacheRedisDatabase);
    	if(RkStrUtil.isNotEmpty(database)) {
    		redisManager.setDatabase(Integer.parseInt(database));
    	}
    	String timeout = env.getProperty(RkConst.session_key.sessionCacheRedisTimeout);
    	if(RkStrUtil.isNotEmpty(timeout)) {
    		redisManager.setTimeout(Integer.parseInt(timeout));
    	}
    	String password = env.getProperty(RkConst.session_key.sessionCacheRedisPassword);
    	if(RkStrUtil.isNotEmpty(password)) {
    		redisManager.setPassword(password);
    	}
    	cacheManager.setRedisManager(redisManager);
    	return cacheManager;
    }
    /**
     * 
     * @author：cavion
     * @描述：开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 2018年6月9日 下午9:05:33
     * @return
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    	DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
    	return defaultAdvisorAutoProxyCreator;
    }
    /**
     * 
     * @author：cavion
     * @描述：保证实现了Shiro内部lifecycle函数的bean执行
     * 2018年6月9日 下午9:09:57
     * @return
     */
    @Bean(name="lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    	return new LifecycleBeanPostProcessor();
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
